/**
 * @file   string_util.hpp
 * @author wolfhead <wolfhead@wolfhead-laptop>
 * @date   Thu Aug 18 11:30:31 2011
 * 
 * @brief  
 * 
 * 
 */
#ifndef ACAPELLA_STRING_STRING_UTIL_HPP
#define ACAPELLA_STRING_STRING_UTIL_HPP

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <sstream>
#include <vector>
#include <map>
#include "string_util_detail.hpp"

namespace acapella
{

class StringUtil
{
public:

    /** 
     * trim both side of the string
     * 
     * @param [in] source intput string
     * @param [out] dest output string
     * @param [in] ignore igonre char list
     */
    static void Trim(
        const std::string& source, 
        std::string& dest, 
        const char * ignore)
    {
        size_t end = source.find_last_not_of(ignore);
        if (end == std::string::npos)
        {
            dest.clear();
            return;
        }
        dest.assign(
            source.begin() + source.find_first_not_of(ignore), 
            source.begin() + end + 1);
        return;
    }

    static std::string Trim(const std::string& source, const char * ignore)
    {
        std::string tmp;
        Trim(source, tmp, ignore);
        return tmp;
    }

    static void TrimLeft(        
        const std::string& source, 
        std::string& dest, 
        const char * ignore)
    {
        dest.assign(source.begin() + source.find_first_not_of(ignore), 
                    source.end());
    }

    static std::string TrimLeft(const std::string& source, const char * ignore)
    {
        std::string tmp;
        TrimLeft(source, tmp, ignore);
        return tmp;
    }

    static void TrimRight(        
        const std::string& source, 
        std::string& dest, 
        const char * ignore)
    {
        size_t end = source.find_last_not_of(ignore);
        if (end == std::string::npos)
        {
            dest.clear();
            return;
        }
        dest.assign(source.begin(), source.begin() + end + 1);
    }

    static std::string TrimRight(const std::string& source, const char * ignore)
    {
        std::string tmp;
        TrimRight(source, tmp, ignore);
        return tmp;
    }

    static void Quote(
        const char * source, 
        const char * left, 
        const char * right, 
        std::string& dest)
    {
        dest.clear();
        dest.append(left)
            .append(source)
            .append(right);
    }

    static std::string Quote(
        const std::string& source,
        const std::string& left = "\"",
        const std::string& right = "\"")
    {
        std::string tmp;
        Quote(source.c_str(), left.c_str(), right.c_str(), tmp);
        return tmp;
    }

    /** 
     * Fill the vec with an array of strings
     * which is substring of str split by charset
     * 
     * @param [in] str source string
     * @param [out] vec dest vector which will
     * @param [in] charset each of the charector in charset will act as delimiter
     * @param [in] limit limit count of substring 
     */
    template<typename InserterType>
    static void Explode(
        const char* str, 
        InserterType inserter, 
        const __detail__::BitmapCharSet& charset)
    {
        const char * current = str;
        
        for (; *current; ++current) 
        {
            if (charset.contain(*current))
            {
                *inserter++ = (std::string(str, current - str));

                str = current + 1;
            }
        }

        *inserter++ = (std::string(str, current - str));
    }

    static std::vector<std::string> Explode(
        const char * str, 
        const __detail__::BitmapCharSet& charset
        )
    {
        std::vector<std::string> tmp;
        Explode(str, std::back_inserter(tmp), charset);
        return tmp;
    }

    static std::vector<std::string> Explode(
        const std::string& str, 
        const __detail__::BitmapCharSet& charset
        )
    {
        std::vector<std::string> tmp;
        Explode(str.c_str(), std::back_inserter(tmp), charset);
        return tmp;
    }

    template<typename IteratorType>
    static void Implode(IteratorType begin, IteratorType end, std::string& str, const char * glue)
    {
        str.clear();

        if (begin == end)
        {
            return;
        }

        str.append(*begin++);
        for (; begin != end;)
        {
            str.append(glue).append(*begin++);
        }
    }

    template<typename Container>
    static std::string Implode(const Container& container, const char * glue)
    {
        std::string tmp;
        Implode(container.begin(), container.end(), tmp, glue);
        return tmp;
    }
    
    static void ParseParam(
        const std::string& str, 
        std::map<std::string, std::string>& param,
        const __detail__::BitmapCharSet& keyValueDelimiter,
        const __detail__::BitmapCharSet& paramDelimiter
        )
    {
        std::vector<std::string> paramVec = 
            Explode(str, paramDelimiter);
        
        std::vector<std::string> pair;

        for (size_t i = 0; i != paramVec.size(); ++i)
        {
            pair.clear();
            Explode(paramVec[i].c_str(), std::back_inserter(pair), keyValueDelimiter);
            if (2 != pair.size())
            {
                continue;
            }

            param.insert(std::make_pair(pair[0], pair[1]));
        }
    }

    static std::map<std::string, std::string> ParseParam(
        const std::string& str, 
        const __detail__::BitmapCharSet& keyValueDelimiter,
        const __detail__::BitmapCharSet& paramDelimiter)
    {
        std::map<std::string, std::string> tmp;
        ParseParam(str, tmp, keyValueDelimiter, paramDelimiter);
        return tmp;
    }

    static void BuildParam(
        const std::map<std::string, std::string>& param,
        std::string& str,
        const char* keyValueDelimiter,
        const char* paramDelimiter
        )
    {
        std::vector<std::string> paramVec;
        std::string tmp;

        size_t keyValueDelimiterLen = strlen(keyValueDelimiter);

        for (std::map<std::string, std::string>::const_iterator it = param.begin();
             it != param.end();
             ++it)
        {
            tmp.clear();
            tmp
                .append(it->first)
                .append(keyValueDelimiter, keyValueDelimiterLen)
                .append(it->second);

            paramVec.push_back(tmp);
        }

        Implode(paramVec.begin()
                , paramVec.end()
                , str
                , paramDelimiter);
    }

    static std::string BuildParam(
        const std::map<std::string, std::string>& param,
        const char* keyValueDelimiter,
        const char* paramDelimiter)
    {
        std::string tmp;
        BuildParam(param, tmp, keyValueDelimiter, paramDelimiter);
        return tmp;
    }


    static void Escape(const char * str, size_t count, std::string& dest)
    {
        static const __detail__::BitmapCharSet charset("\a\b\f\n\r\t\v\\\?\'\"\0");

        dest.clear();
        dest.reserve(count);

        const char * current = str;
        const char * end = current + count;
        for (; current != end; ++current)
        {
            if (charset.contain(*current))
            {
                if (current - str)
                {
                    dest.append(str, current-str);
                }
                switch (*current)
                {
                case '\a' : dest.append("\\a");break;
                case '\b' : dest.append("\\b");break;
                case '\f' : dest.append("\\b");break;
                case '\n' : dest.append("\\n");break;
                case '\r' : dest.append("\\r");break;
                case '\t' : dest.append("\\t");break;
                case '\v' : dest.append("\\v");break;
                case '\\' : dest.append("\\\\");break;
                case '\?' : dest.append("\\\?");break;
                case '\'' : dest.append("\\\'");break;
                case '\"' : dest.append("\\\"");break;
                case '\0' : dest.append("\\\0");break;
                default:
                    dest.append(1, *current);
                    break;
                }

                str = current + 1;
            }
        }

        if (current - str)
        {
            dest.append(str, current-str);
        }
        return;
    }


    static std::string Escape(const char * str, size_t count)
    {
        std::string tmp;
        Escape(str, count, tmp);
        return tmp;
    }


    static std::string Escape(const char * str)
    {
        std::string tmp;
        Escape(str, strlen(str), tmp);
        return tmp;
    }

    static std::string Escape(const std::string& str)
    {
        std::string tmp;
        Escape(str.c_str(), str.size(), tmp);
        return tmp;
    }

    static void Unescape(const char* str, size_t count, std::string& dest)
    {
        dest.clear();
        dest.reserve(count);

        const char * current = str;
        const char * end = str + count;
        for (; current != end; ++current)
        {
            if (*current == '\\' && current + 1 != end)
            {
                dest.append(str, current-str);

                ++current;
                
                switch(*current)
                {
                case 'a' : dest.append(1, '\a'); break;
                case 'b' : dest.append(1, '\b'); break;
                case 'f' : dest.append(1, '\f'); break;
                case 'n' : dest.append(1, '\n'); break;
                case 'r' : dest.append(1, '\r'); break;
                case 't' : dest.append(1, '\t'); break;
                case 'v' : dest.append(1, '\v'); break;
                case '\\' : dest.append(1, '\\'); break;
                case '?' : dest.append(1, '\?'); break;
                case '\'' : dest.append(1, '\''); break;
                case '\"' : dest.append(1, '\"'); break;
                case '\0' : dest.append(1, '\0'); break;
                default :
                    dest.append(1, *current);
                    break;
                }

                str = current + 1;
            }
        }

        dest.append(str, current-str);

        return;
    }

    static std::string Unescape(const char * str ,size_t count)
    {
        std::string tmp;
        Unescape(str, count, tmp);
        return tmp;
    }

    static std::string Unescape(const char * str)
    {
        std::string tmp;
        Unescape(str, strlen(str), tmp);
        return tmp;
    }

    static std::string Unescape(const std::string& str)
    {
        std::string tmp;
        Unescape(str.c_str(), str.size(), tmp);
        return tmp;
    }

    static void ReplaceAll(
        const std::string& source, 
        std::string& dest, 
        const std::string& oldData, 
        const std::string& newData)
    {
        dest = source;
        size_t pos = 0; 
        while (true)
        {
            if((pos = dest.find(oldData, pos)) != std::string::npos)  
            {
                dest.replace(pos, oldData.length(), newData);   
            }
            else
            {
                break;
            }
        }
    }

    static std::string ReplaceAll(
        const std::string& source, 
        const std::string& oldData,
        const std::string& newData)
    {
        std::string tmp;
        ReplaceAll(source, tmp, oldData, newData);
        return tmp;
    }


    template<typename T>
    static T ToValue(const char * str, T default_value = 0)
    {
        return __detail__::_StringToValueImpl<T>::toValue(str, default_value);
    }

    template<typename T>
    static T ToValue(const std::string& str, T default_value = 0)
    {
        return __detail__::_StringToValueImpl<T>::toValue(str.c_str(), default_value);
    }

    template<typename T>
    static std::string ToString(T value)
    {
        std::ostringstream oss;
        oss << value;
        return oss.str();
    }

    template<size_t size>
    static void Format(std::string& dest, const char * pFormat, ...)
    {
        char buffer[size];
        va_list argList;
        va_start(argList, pFormat);
        size_t ret = vsnprintf(buffer, size, pFormat, argList);
        va_end(argList);
        dest.assign(buffer, ret);
    }

    template<size_t size>
    static std::string Format(const char * pFormat, ...)
    {
        char buffer[size];
        va_list argList;
        va_start(argList, pFormat);
        size_t ret = vsnprintf(buffer, size, pFormat, argList);
        va_end(argList);
        return std::string(buffer, ret);
    }

    static void Bin2Hex(
        unsigned char bin, 
        unsigned char& high, 
        unsigned char& low)
    {
        return __detail__::__ConvertImpl::Bin2Hex(bin, high, low);
    }

    static void Hex2Bin(
        unsigned char high, 
        unsigned char low, 
        unsigned char& bin)
    {
        return __detail__::__ConvertImpl::Hex2Bin(high, low, bin);
    }

    static void Bin2Oct(
        unsigned char bin, 
        unsigned char& c1, 
        unsigned char& c2, 
        unsigned char& c3)
    {
        return __detail__::__ConvertImpl::Bin2Oct(bin, c1, c2, c3);
    }

    static void Oct2Bin(
        unsigned char c1, 
        unsigned char c2, 
        unsigned char c3, 
        unsigned char& bin)
    {
        return __detail__::__ConvertImpl::Oct2Bin(c1, c2, c3, bin);
    }

    static void Bin2Hex(const void * bin, size_t size, std::string& hex)
    {
        hex.clear();
        hex.reserve(size << 1);
        
        unsigned char high;
        unsigned char low;

        for (size_t i = 0; i != size; ++i)
        {
            Bin2Hex(((const unsigned char *)bin)[i], high, low);
            hex.append(1, high).append(1, low);
        }
    }

    static std::string Bin2Hex(const void * bin, size_t size)
    {
        std::string tmp;
        Bin2Hex(bin, size, tmp);
        return tmp;
    }

    static void Hex2Bin(const char * hex, size_t size, std::string& bin)
    {
        if (size << (sizeof(size_t) * 8 - 1))
        {
            bin.clear();
            bin.reserve((size >> 1) + 1);

            unsigned char b;

            Hex2Bin('0', hex[0], b);
            bin.append(1, b);

            for (size_t i = 1; i != size; i+=2)
            {
                Hex2Bin(hex[i], hex[i + 1], b);
                bin.append(1, b);
            }
        }
        else
        {
            bin.clear();
            bin.reserve(size >> 1);

            unsigned char b;

            for (size_t i = 0; i != size; i+=2)
            {
                Hex2Bin(hex[i], hex[i + 1], b);
                bin.append(1, b);
            }
        }
    }

    static std::string Hex2Bin(const char * hex)
    {
        std::string tmp;
        Hex2Bin(hex, strlen(hex), tmp);
        return tmp;
    }

    static std::string Hex2Bin(const std::string& hex)
    {
        std::string tmp;
        Hex2Bin(hex.data(), hex.size(), tmp);
        return tmp;
    }

    static void ToUpper(const char * str, size_t size, std::string& output)
    {
        output.assign(str, size);
        for (size_t i = 0; i != size; ++i)
        {
            __detail__::__ConvertImpl::ToUpper((unsigned char)output[i], (unsigned char&)output[i]);
        }
    }

    static std::string ToUpper(const std::string str)
    {
        std::string tmp;
        ToUpper(str.data(), str.size(), tmp);
        return tmp;
    }

    static void ToLower(const char * str, size_t size, std::string& output)
    {
        output.assign(str, size);
        for (size_t i = 0; i != size; ++i)
        {
            __detail__::__ConvertImpl::ToLower((unsigned char)output[i], (unsigned char&)output[i]);
        }
    }

    static std::string ToLower(const std::string str)
    {
        std::string tmp;
        ToLower(str.data(), str.size(), tmp);
        return tmp;
    }
};

}//namespace acapella

#endif
